package com.yfp.client.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.binarywang.wxpay.bean.notify.WxPayPartnerNotifyV3Result;
import com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderV3Result;
import com.hy.corecode.idgen.WFGIdGenerator;
import com.yfp.client.domain.*;
import com.yfp.client.domain.*;
import com.yfp.client.domain.dto.ClientBillDTO;
import com.yfp.client.domain.dto.ClientBillReqDTO;
import com.yfp.client.domain.dto.CreateWxPayOrderDTO;
import com.yfp.client.domain.dto.StoredResultsDTO;
import com.yfp.client.domain.vo.ClientBillVO;
import com.yfp.client.enums.ClientBillStatusEnum;
import com.yfp.client.enums.ClientBillTypeEnum;
import com.yfp.client.enums.StoredValueActivityStateEnum;
import com.yfp.client.enums.WxPayAttach;
import com.yfp.client.mapper.ClientBillMapper;
import com.yfp.client.service.*;
import com.yfp.client.service.*;
import com.yfp.client.util.RedisDistributedLock;
import com.yfp.client.util.ResponseEnum;
import com.yfp.client.util.SecurityUtils;
import com.yfp.common.core.enums.PayTypeEnum;
import com.yfp.common.core.exception.ServiceException;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;

/**
 *
 * @description 针对表【client_bill(用户余额明细)】的数据库操作Service实现
 * @createDate 2024-11-22 10:15:05
 */
@Slf4j
@Service
public class ClientBillServiceImpl extends ServiceImpl<ClientBillMapper, ClientBill> implements ClientBillService {

    @Resource
    private RedisTemplate<String, String> redisTemplate;
    @Resource
    private WFGIdGenerator wFGIdGenerator;
    @Resource
    private ClientService clientService;
    @Resource
    private ClientBillMapper clientBillMapper;
    @Resource
    private ClientCouponService clientCouponService;
    @Resource
    private StoredValueActivityService storedValueActivityService;
    @Resource
    private StoredValueActivityDetailService storedValueActivityDetailService;
    @Resource
    private PayService payService;
    @Resource
    private SysDictDataService sysDictDataService;
    @Resource
    private OrderGiftGoldService orderGiftGoldService;

    @Override
    public List<ClientBillVO> getClientBillList(ClientBillReqDTO clientBill) {
        String clientId = SecurityUtils.getClientId();

        // 查询类型[0:全部,1:储值,2:消费]
        HashMap<Integer, List<Integer>> typeHashMap = MapUtil.newHashMap();
        typeHashMap.put(0, Arrays.asList(ClientBillTypeEnum.RECHARGE.getCode(), ClientBillTypeEnum.CONSUME.getCode(), ClientBillTypeEnum.STORED_REFUND.getCode(), ClientBillTypeEnum.COMBO_REFUND.getCode()));
        typeHashMap.put(1, Arrays.asList(ClientBillTypeEnum.RECHARGE.getCode()));
        typeHashMap.put(2, Arrays.asList(ClientBillTypeEnum.CONSUME.getCode()));

        ClientBillDTO clientBillDTO = new ClientBillDTO();
        clientBillDTO.setClientId(clientId);
        clientBillDTO.setTypeList(typeHashMap.get(clientBill.getType()));
        if (Objects.nonNull(clientBill.getDate())) {
            clientBillDTO.setStartTime(DateUtil.beginOfMonth(clientBill.getDate()));
            clientBillDTO.setEndTime(DateUtil.endOfMonth(clientBill.getDate()));
        }
        clientBillDTO.setStatus(ClientBillStatusEnum.FINISH.getCode());

        List<ClientBill> clientBills = clientBillMapper.selectClientBillList(clientBillDTO);
        List<ClientBillVO> clientBillVOList = BeanUtil.copyToList(clientBills, ClientBillVO.class).stream().peek(item -> {
            // 交易金额 根据交易类型 转变为负数
            if (ClientBillTypeEnum.CONSUME.getCode().equals(item.getType()) || ClientBillTypeEnum.STORED_REFUND.getCode().equals(item.getType())) {
                item.setMoney(item.getMoney().negate());
                item.setGiftGold(item.getGiftGold().negate());
            }
        }).sorted(Comparator.comparing(ClientBillVO::getCreateTime, Comparator.reverseOrder())).collect(Collectors.toList());

        return clientBillVOList;
    }

    @Override
    public String rechargeSettle(Long activityId, Long activityDetailId) {
        String clientId = SecurityUtils.getClientId();

        StoredValueActivity storedValueActivity = storedValueActivityService.getById(activityId);
        boolean notIn = !DateUtil.isIn(DateUtil.date(), storedValueActivity.getStartTime(), storedValueActivity.getEndTime());
        boolean invalid = !StoredValueActivityStateEnum.NORMAL.getCode().equals(storedValueActivity.getStatus());
        if (notIn || invalid) {
            throw new ServiceException(ResponseEnum.STORED_ACTIVITY_INVALID.getMsg(), ResponseEnum.STORED_ACTIVITY_INVALID.getCode());
        }

        StoredValueActivityDetail storedValueActivityDetail = storedValueActivityDetailService.getById(activityDetailId);
        Boolean firstStored = this.isFirstStored();

        ClientBill clientBill = new ClientBill();
        clientBill.setClientId(clientId);
        clientBill.setType(ClientBillTypeEnum.RECHARGE.getCode());
        clientBill.setMoney(storedValueActivityDetail.getMoney());
        clientBill.setGiftGold(storedValueActivityDetail.getGiftGold());
        if (firstStored) {
            // 首冲赠送金增加
            StoredValueActivityDetail firstStoredDetail = Optional.ofNullable(storedValueActivityDetailService.listFirstStoredByActivityId(activityId)).orElse(new StoredValueActivityDetail());
            clientBill.setGiftGold(NumberUtil.add(clientBill.getGiftGold(), firstStoredDetail.getGiftGold()));
        }
        clientBill.setOrderSn(String.valueOf(wFGIdGenerator.next()));
        clientBill.setCreateTime(DateUtil.date());
        clientBill.setCurrentMoney(null);
        clientBill.setCurrentGiftGold(null);
        clientBill.setPayTradeNo(null);
        clientBill.setPayType(PayTypeEnum.WeChatPay.getCode());
        clientBill.setPrepayId(null);
        clientBill.setGiftRatio(NumberUtil.div(storedValueActivityDetail.getGiftGold(), NumberUtil.add(storedValueActivityDetail.getMoney(), storedValueActivityDetail.getGiftGold())).setScale(6, RoundingMode.UP));
        clientBill.setActivityId(storedValueActivityDetail.getActivityId());
        clientBill.setStatus(ClientBillStatusEnum.WAIT_FINISH.getCode());
        clientBill.setActivityDetailId(storedValueActivityDetail.getId());
        clientBill.setCurrentBillMoney(clientBill.getMoney());
        clientBill.setCurrentBillGiftGold(clientBill.getGiftGold());

        clientBillMapper.insert(clientBill);
        return clientBill.getOrderSn();
    }

    @Override
    public WxPayUnifiedOrderV3Result.JsapiResult rechargeSettlePayment(String orderSn) {
        String clientId = SecurityUtils.getClientId();

        Client client = clientService.getById(clientId);
        ClientBill clientBill = clientBillMapper.selectByOrderSn(orderSn);
        String subMchId = sysDictDataService.getDefaultReceivingAccount();

        // 创建微信支付订单
        CreateWxPayOrderDTO createWxPayOrderDTO = new CreateWxPayOrderDTO();
        createWxPayOrderDTO.setDescription("储值");
        createWxPayOrderDTO.setTimeExpire(DateUtil.date(clientBill.getCreateTime()).offset(DateField.MINUTE, 3));
        createWxPayOrderDTO.setTotalPrice(clientBill.getMoney());
        createWxPayOrderDTO.setSubMchId(subMchId);
        createWxPayOrderDTO.setAttach(WxPayAttach.STORED_VALUE_TYPE.getValue());
        createWxPayOrderDTO.setOpenid(client.getOpenId());
        createWxPayOrderDTO.setOutTradeNo(clientBill.getOrderSn());

        // 调用微信支付
        WxPayUnifiedOrderV3Result.JsapiResult wxJsapiPayOrder = payService.createWxJsapiPayOrder(createWxPayOrderDTO);
        String wxPrepayId = StrUtil.replace(wxJsapiPayOrder.getPackageValue(), "prepay_id=", "");

        clientBillMapper.updatePrepayIdById(clientBill.getId(), wxPrepayId);
        return wxJsapiPayOrder;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void orderCreateNotify(WxPayPartnerNotifyV3Result wxPayPartnerNotifyV3Result) {
        String orderId = wxPayPartnerNotifyV3Result.getResult().getOutTradeNo();

        RedisDistributedLock redisDistributedLock = new RedisDistributedLock(redisTemplate, "client:wxpay:orderCreateNotify:" + orderId, 10);
        try {
            redisDistributedLock.lock();
            ClientBill clientBill = clientBillMapper.selectByOrderSn(orderId);
            if (ClientBillStatusEnum.FINISH.getCode().equals(clientBill.getStatus())) {
                return;
            }
            StoredValueActivity storedValueActivity = storedValueActivityService.getById(clientBill.getActivityId());
            if (!StoredValueActivityStateEnum.NORMAL.getCode().equals(storedValueActivity.getStatus())) {
                // 活动状态非正常
                return;
            }
            Boolean firstStored = this.isFirstStored(clientBill.getClientId());
            Client client = clientService.getById(clientBill.getClientId());

            // 储值金额
            client.setBalance(NumberUtil.add(client.getBalance(), clientBill.getMoney()));
            // 赠送消费金额
            client.setGiftGold(NumberUtil.add(client.getGiftGold(), clientBill.getGiftGold()));
            clientService.edit(client);
            // 订单状态更新
            clientBill.setPayTradeNo(wxPayPartnerNotifyV3Result.getResult().getTransactionId());
            clientBill.setStatus(ClientBillStatusEnum.FINISH.getCode());
            clientBill.setOrderSn(orderId);
            clientBill.setCurrentMoney(client.getBalance());
            clientBill.setCurrentGiftGold(client.getGiftGold());
            clientBillMapper.updateByOrderSn(clientBill);

            // 额外储值权益
            List<StoredValueActivityDetail> marCouponList = new ArrayList<>();
            marCouponList.add(storedValueActivityDetailService.getById(clientBill.getActivityDetailId()));
            if (firstStored) {
                marCouponList.add(storedValueActivityDetailService.listFirstStoredByActivityId(clientBill.getActivityId()));
            }
            marCouponList.stream().filter(item -> Objects.nonNull(item.getMarCouponId())).forEach(item -> {
                for (Long l = 0L; l < item.getMarCouponCount(); l++) {
                    ResponseEnum responseEnum = clientCouponService.verifyCoupon(item.getMarCouponId(), clientBill.getClientId(), null, null, null,clientBill.getId());
                    log.info("储值有礼优惠券发放,客户id{},优惠券id{},结果：{}", client.getId(), item.getMarCouponId(), responseEnum.getMsg());
                }
            });
        } catch (Exception e) {
            log.error("orderCreateNotify:出现异常， 异常信息：{}", e.getMessage());
        } finally {
            redisDistributedLock.unlock();
        }
    }

    @Override
    public Boolean isFirstStored() {
        String clientId = SecurityUtils.getClientId();
        return isFirstStored(clientId);
    }

    @Override
    public Boolean isFirstStored(String clientId) {
        // 查询是否存在finish状态的订单
        LambdaQueryWrapper<ClientBill> query = Wrappers.lambdaQuery(ClientBill.class);
        query.eq(ClientBill::getClientId, clientId);
        query.eq(ClientBill::getStatus, ClientBillStatusEnum.FINISH.getCode());
        boolean exists = clientBillMapper.exists(query);
        return !exists;
    }

    @Override
    public List<ClientBill> getClientBillRechargeList() {
        String clientId = SecurityUtils.getClientId();

        ClientBillDTO clientBillDTO = new ClientBillDTO();
        clientBillDTO.setClientId(clientId);
        clientBillDTO.setTypeList(Collections.singletonList(ClientBillTypeEnum.RECHARGE.getCode()));
        clientBillDTO.setStatus(ClientBillStatusEnum.FINISH.getCode());

        List<ClientBill> clientBills = clientBillMapper.selectClientBillList(clientBillDTO).stream().sorted(Comparator.comparing(ClientBill::getCreateTime)).collect(Collectors.toList());
        return clientBills;
    }

    @Override
    public void balanceRefundOrder(OrderCombo orderCombo, Client client) {
        List<OrderGiftGold> orderGiftGolds = orderGiftGoldService.listByOrderId(orderCombo.getOrderId());
        BigDecimal money = orderGiftGolds.stream().map(OrderGiftGold::getMoney).reduce(BigDecimal.ZERO, BigDecimal::add);
        BigDecimal giftGold = orderGiftGolds.stream().map(OrderGiftGold::getGiftGold).reduce(BigDecimal.ZERO, BigDecimal::add);

        ClientBill clientBillNew = new ClientBill();
        clientBillNew.setClientId(client.getId());
        clientBillNew.setType(ClientBillTypeEnum.COMBO_REFUND.getCode());
        clientBillNew.setMoney(money);
        clientBillNew.setGiftGold(giftGold);
        clientBillNew.setOrderSn(orderCombo.getOrderId());
        clientBillNew.setCreateTime(DateUtil.date());
        clientBillNew.setCurrentMoney(client.getBalance());
        clientBillNew.setCurrentGiftGold(client.getGiftGold());
        clientBillNew.setPayTradeNo(null);
        clientBillNew.setPayType(PayTypeEnum.BalancePay.getCode());
        clientBillNew.setPrepayId(null);
        clientBillNew.setGiftRatio(null);
        clientBillNew.setActivityId(null);
        clientBillNew.setStatus(ClientBillStatusEnum.FINISH.getCode());
        clientBillNew.setActivityDetailId(null);
        clientBillNew.setCurrentBillMoney(null);
        clientBillNew.setCurrentBillGiftGold(null);
        clientBillMapper.insert(clientBillNew);


        List<Long> clientBillIds = orderGiftGolds.stream().map(OrderGiftGold::getClientBillId).collect(Collectors.toList());
        List<ClientBill> clientBills = clientBillMapper.selectBatchIds(clientBillIds);
        Map<Long, ClientBill> clientBillMapById = clientBills.stream().collect(Collectors.toMap(ClientBill::getId, s -> s));
        orderGiftGolds.stream().forEach(item -> {
            ClientBill clientBill = clientBillMapById.get(item.getClientBillId());
            clientBill.setCurrentBillMoney(NumberUtil.add(clientBill.getCurrentBillMoney(), item.getMoney()));
            clientBill.setCurrentBillGiftGold(NumberUtil.add(clientBill.getCurrentBillGiftGold(), item.getGiftGold()));
            clientBillMapper.updateById(clientBill);
        });
    }

    @Override
    public void balanceRefundOrder(Client client, List<OrderGiftGold> orderGiftGolds) {
        List<Long> clientBillIds = orderGiftGolds.stream().map(OrderGiftGold::getClientBillId).collect(Collectors.toList());
        List<ClientBill> clientBills = clientBillMapper.selectBatchIds(clientBillIds);
        Map<Long, ClientBill> clientBillMapById = clientBills.stream().collect(Collectors.toMap(ClientBill::getId, s -> s));
        orderGiftGolds.stream().forEach(item -> {
            ClientBill clientBill = clientBillMapById.get(item.getClientBillId());
            clientBill.setCurrentBillMoney(NumberUtil.add(clientBill.getCurrentBillMoney(), item.getMoney()));
            clientBill.setCurrentBillGiftGold(NumberUtil.add(clientBill.getCurrentBillGiftGold(), item.getGiftGold()));
            clientBillMapper.updateById(clientBill);
        });
    }

    @Override
    public void balanceRefundInsertOrder(String orderId, Client client, List<OrderGiftGold> orderGiftGolds) {
        BigDecimal money = orderGiftGolds.stream().map(OrderGiftGold::getMoney).reduce(BigDecimal.ZERO, BigDecimal::add);
        BigDecimal giftGold = orderGiftGolds.stream().map(OrderGiftGold::getGiftGold).reduce(BigDecimal.ZERO, BigDecimal::add);

        ClientBill clientBillNew = new ClientBill();
        clientBillNew.setClientId(client.getId());
        clientBillNew.setType(ClientBillTypeEnum.COMBO_REFUND.getCode());
        clientBillNew.setMoney(money);
        clientBillNew.setGiftGold(giftGold);
        clientBillNew.setOrderSn(orderId);
        clientBillNew.setCreateTime(DateUtil.date());
        clientBillNew.setCurrentMoney(client.getBalance());
        clientBillNew.setCurrentGiftGold(client.getGiftGold());
        clientBillNew.setPayTradeNo(null);
        clientBillNew.setPayType(PayTypeEnum.BalancePay.getCode());
        clientBillNew.setPrepayId(null);
        clientBillNew.setGiftRatio(null);
        clientBillNew.setActivityId(null);
        clientBillNew.setStatus(ClientBillStatusEnum.FINISH.getCode());
        clientBillNew.setActivityDetailId(null);
        clientBillNew.setCurrentBillMoney(null);
        clientBillNew.setCurrentBillGiftGold(null);
        clientBillMapper.insert(clientBillNew);
    }

    /**
     * 插入退款记录
     *
     * @param clientBillNew
     */
    @Override
    public void createRefund(ClientBill clientBillNew) {
        clientBillNew.setId(null);
        clientBillNew.setType(ClientBillTypeEnum.STORED_REFUND.getCode());
        clientBillNew.setCreateTime(DateUtil.date());
        clientBillNew.setMoney(clientBillNew.getCurrentBillMoney());
        clientBillNew.setGiftGold(clientBillNew.getCurrentBillGiftGold());
        clientBillNew.setCurrentMoney(BigDecimal.valueOf(0));
        clientBillNew.setCurrentGiftGold(BigDecimal.valueOf(0));
        clientBillNew.setPayTradeNo(null);
        clientBillNew.setPayType(PayTypeEnum.BalancePay.getCode());
        clientBillNew.setPrepayId(null);
        clientBillNew.setGiftRatio(null);
        clientBillNew.setActivityId(null);
        clientBillNew.setStatus(ClientBillStatusEnum.FINISH.getCode());
        clientBillNew.setActivityDetailId(null);
        clientBillNew.setCurrentBillMoney(null);
        clientBillNew.setCurrentBillGiftGold(null);
        clientBillMapper.insert(clientBillNew);
    }

    /**
     * 根据订单号获取数据
     *
     * @param orderId
     * @return
     */
    @Override
    public ClientBill getByOrderSn(String orderId) {
        return clientBillMapper.getByOrderSn(orderId);
    }

    @Override
    public void createBalancePay(String orderId, StoredResultsDTO storedResultsDTO, List<ClientBill> clientBills, Client client) {
        // 新增消费余额账单
        ClientBill clientBillNew = new ClientBill();
        clientBillNew.setClientId(client.getId());
        clientBillNew.setType(ClientBillTypeEnum.CONSUME.getCode());
        clientBillNew.setMoney(storedResultsDTO.getMoney());
        clientBillNew.setGiftGold(storedResultsDTO.getGiftMoney());
        clientBillNew.setOrderSn(orderId);
        clientBillNew.setCreateTime(DateUtil.date());
        clientBillNew.setCurrentMoney(client.getBalance());
        clientBillNew.setCurrentGiftGold(client.getGiftGold());
        clientBillNew.setPayTradeNo(null);
        clientBillNew.setPayType(PayTypeEnum.BalancePay.getCode());
        clientBillNew.setPrepayId(null);
        clientBillNew.setGiftRatio(null);
        clientBillNew.setActivityId(null);
        clientBillNew.setStatus(ClientBillStatusEnum.FINISH.getCode());
        clientBillNew.setActivityDetailId(null);
        clientBillNew.setCurrentBillMoney(null);
        clientBillNew.setCurrentBillGiftGold(null);
        clientBillMapper.insert(clientBillNew);

        for (ClientBill clientBill : clientBills) {
            clientBillMapper.updateById(clientBill);
        }
    }

    /**
     * 修改状态为待完成
     * @param id
     */
    @Override
    public void updateStatus(Long id,Integer status) {
        clientBillMapper.updateStatus(id,status);
    }

    /**
     * 根据客户id获取充值记录
     *
     * @param clientId
     * @return
     */
    @Override
    public List<ClientBill> balanceNotZero(String clientId) {
        List<ClientBill> cbList = clientBillMapper.balanceNotZero(clientId);
        return cbList;
    }

    /**
     * 清空储值记录的余额和赠送金余额
     *
     * @param cbId
     */
    @Override
    public void emptyCurrentBillMoney(Long cbId) {
        clientBillMapper.emptyCurrentBillMoney(cbId);
    }
}




